package com.gitee.apanlh.util.base;

import com.gitee.apanlh.exp.CallRuntimeException;
import com.gitee.apanlh.util.func.FuncCall;
import com.gitee.apanlh.util.func.FuncExecute;
import com.gitee.apanlh.util.reflection.ClassTypeUtils;

import java.util.Collection;
import java.util.Map;

/**	
 * 	选择器-值等同
 * 	<br>单匹配值值多匹配值值模式
 * 	<br>提供三种回调值模式,1.直接返回值，2.回调函数-带返回值，3.自定义函数-无返回值
 * 	<br>用于省略存在多个条件的if判断并且操作set或者get或者需要某一个回调值，
 * 	<pre>
 * 	代码示例:
 * 	{@code
 *  原:
 *      String a = "1"
 *
 *      if ("1".equals(a)) {
 * 	    	//	执行1
 * 	    }
 * 	    if ("2".equals(a)) {
 * 	        //	执行2
 * 	    }
 * 	    后:
 * 	    //	Eq类中的封装方法
 * 	    //	一种方式
 * 	    Eq.call("1", "a", "3");
 * 	    Eq.call("1", "a", () -> 执行方法());
 * 	    Eq.call("1", "a", () -> "1");
 *		//	一种方式   提供三种模式，1.直接返回值，2.回调函数-带返回值，3.自定义函数-无返回值
 * 	    Eq.matcher().when().when().end();
 *
 * 	    //	单匹配值方式
 * 	    //	一种方式   提供三种模式，1.直接返回值，2.回调函数-带返回值，3.自定义函数-无返回值
 * 	    1.直接返回值
 * 	    ChooseEq.create().when("1", a, "2").when(xxx, xxx, xxx).end();
 *		1.如果值未相等，返回一个结果值
 * 	    ChooseEq.create().when("2", a, "2").when(xxx, xxx, xxx).end("2");
 * 	    2.回调函数-带返回值
 * 	    ChooseEq.create().when("2", a, () -> "2").when(xxx, xxx, xxx).end();
 * 	    3.自定义函数-无返回值
 * 	    ChooseEq.create().when("2", a, () -> function()).when(xxx, xxx, xxx).end();
 *
 *		//	多匹配值方式(一般用于函数式赋值)	其他方式自行探索
 *  	ChooseEq.create()
 *  		.when("1", a, () -> set(xxx))
 *  		.when("2", b, () -> set(xxx))
 *  		.when("3", c, () -> set(xxx))
 *  		.when("4", d, () -> set(xxx))
 *  	.end();
 * 	}
 * 	</pre>
 * 	@author Pan
 */
public class ChooseEq<T, R> {

	/** 匹配结果 */
	private boolean hasMatch;
	/** 多匹配值模式 */
	private boolean multiMode;
	/** 回调值 */
	private R callValue;
	private T value;

	/**
	 * 	构造函数
	 *
	 * 	@author Pan
	 */
	private ChooseEq() {
		//	不允许外部实例
		super();
	}

	/**
	 * 	构造函数-自定义多匹配值模式
	 *	<br>多匹配值模式:开启此模式时，在调用when()时如果已经匹配成功, 将继续调用下一个链式方法，在结尾调用end()的时候，将返回最后一个值匹配成功的结果
	 *	<br>单匹配值模式:开启此模式时，在调用when()时如果已经匹配成功，将不会在继续调用下一个链式方法，调用end()时只会返回第一次的值匹配成功的结果
	 *
	 * 	@author Pan
	 * 	@param 	multiMode	true开启多匹配值模式
	 */
	public ChooseEq(boolean multiMode) {
		super();
		this.multiMode = multiMode;
	}

	/**
	 * 	构造函数-自定义多匹配值模式
	 *	<br>多匹配值模式:开启此模式时，在调用when()时如果已经匹配成功, 将继续调用下一个链式方法，在结尾调用end()的时候，将返回最后一个值匹配成功的结果
	 *	<br>单匹配值模式:开启此模式时，在调用when()时如果已经匹配成功，将不会在继续调用下一个链式方法，调用end()时只会返回第一次的值匹配成功的结果
	 *
	 * 	@author Pan
	 * 	@param 	value		默认匹配值
	 * 	@param 	multiMode	true开启多匹配值模式
	 */
	public ChooseEq(T value, boolean multiMode) {
		super();
		this.value = value;
		this.multiMode = multiMode;
	}

	/**
	 * 	条件
	 * 	<br>如果条件equals为true，将返回callValue值
	 *
	 * 	@author Pan
	 * 	@param 	eqValue		比较值
	 * 	@param 	callValue	回调值
	 * 	@return	ChooseEq
	 */
	public ChooseEq<T, R> when(T eqValue, R callValue) {
		match(this.value, eqValue, () -> this.callValue = callValue);
		return this;
	}

	/**
	 * 	条件-自定义回调函数
	 * 	<br>如果条件equals为true，将返回自定义回调函数中的值-有返回值
	 *
	 * 	@author Pan
	 * 	@param 	eqValue		比较值
	 * 	@param 	func		回调函数
	 * 	@return	ChooseEq
	 */
	public ChooseEq<T, R> when(T eqValue, FuncCall<R> func) {
		match(this.value, eqValue, () -> {
			try {
				this.callValue = func.call();
			} catch (Exception e) {
				throw new CallRuntimeException(e.getMessage(), e);
			}
		});
		return this;
	}

	/**
	 * 	条件-自定义执行函数
	 * 	<br>如果条件equals为true，将执行自定义函数-无返回值
	 *
	 * 	@author Pan
	 * 	@param 	eqValue		比较值
	 * 	@param 	func		执行函数
	 * 	@return	ChooseEq
	 */
	public ChooseEq<T, R> when(T eqValue, FuncExecute func) {
		match(this.value, eqValue, func);
		return this;
	}

	/**
	 * 	条件
	 * 	<br>如果条件equals为true，将返回callValue值
	 *
	 * 	@author Pan
	 * 	@param 	value		值
	 * 	@param 	eqValue		比较值
	 * 	@param 	callValue	回调值
	 * 	@return	ChooseEq
	 */
	public ChooseEq<T, R> when(T value, T eqValue, R callValue) {
		match(value, eqValue, () -> this.callValue = callValue);
		return this;
	}

	/**
	 * 	条件-自定义回调函数
	 * 	<br>如果条件equals为true，将返回自定义回调函数中的值-有返回值
	 *
	 * 	@author Pan
	 * 	@param 	value		值
	 * 	@param 	eqValue		比较值
	 * 	@param 	func		回调函数
	 * 	@return	ChooseEq
	 */
	public ChooseEq<T, R> when(T value, T eqValue, FuncCall<R> func) {
		match(value, eqValue, () -> {
			try {
				this.callValue = func.call();
			} catch (Exception e) {
				throw new CallRuntimeException(e.getMessage(), e);
			}
		});
		return this;
	}

	/**
	 * 	条件-自定义执行函数
	 * 	<br>如果条件equals为true，将执行自定义函数-无返回值
	 *
	 * 	@author Pan
	 * 	@param 	value		值
	 * 	@param 	eqValue		比较值
	 * 	@param 	func		执行函数
	 * 	@return	ChooseEq
	 */
	public ChooseEq<T, R> when(T value, T eqValue, FuncExecute func) {
		match(value, eqValue, func);
		return this;
	}

	/**
	 *	结束-返回回调值
	 *	<br>多匹配值模式:开启此模式时，无论是否值等同已经匹配成功都将返回最后一个值匹配成功的结果
	 *	<br>单匹配值模式:开启此模式时，只会返回第一次的值匹配成功的结果
	 *
	 * 	@author Pan
	 * 	@param 	<R>	返回数据类型
	 * 	@return	R
	 */
	public <R> R end() {
		if (this.callValue == null) {
			return null;
		}
		return (R) this.callValue;
	}

	/**
	 *	结束-自定义返回值
	 *	<br>多匹配值模式:开启此模式时，无论是否值等同已经匹配成功都将返回自定义的值
	 *	<br>单匹配值模式:开启此模式时，只会返回第一次的值匹配成功的结果
	 *
	 * 	@author Pan
	 * 	@param 	<R>		返回数据类型
	 * 	@param 	value	返回值
	 * 	@return	R
	 */
	public <R> R end(R value) {
		if (!this.multiMode && this.hasMatch) {
			return (R) this.callValue;
		}
		return value;
	}

	/**
	 *	结束-返回回调值
	 *	<br>多匹配值模式:开启此模式时，无论是否值等同已经匹配成功都将返回回调函数中的值
	 *	<br>单匹配值模式:开启此模式时，只会返回第一次的值匹配成功的结果
	 *
	 * 	@author Pan
	 * 	@param 	func	回调函数
	 * 	@return	T
	 */
	public <R> R end(FuncCall<R> func) {
		if (!this.multiMode && this.hasMatch) {
			return (R) this.callValue;
		}
		try {
			return func.call();
		} catch (Exception e) {
			throw new CallRuntimeException(e.getMessage(), e);
		}
	}

	/**
	 *	结束-将执行自定义执行函数-无返回值
	 *	<br>多匹配值模式:开启此模式时，无论是否值等同已经匹配成功都将执行自定义执行函数
	 *	<br>单匹配值模式:开启此模式时，如果值等同的情况下，将不会调用自定义执行函数
	 *
	 * 	@author Pan
	 * 	@param 	func	回调函数
	 */
	public void end(FuncExecute func) {
		if (hasMatch) {
			return ;
		}
		func.execute();
	}

	/**
	 *	匹配结果-如果
	 *	<br>多匹配值模式:开启此模式时，在调用when()时如果值已经匹配成功, 将继续调用下一个链式方法，在结尾调用end()的时候将返回最后一个值匹配结果
	 *	<br>单匹配值模式:开启此模式时，在调用when()时如果值等同的情况下，将不会在继续调用下一个链式方法，调用end()时只会返回第一次的值匹配成功的结果
	 *
	 * 	@param value	值
	 * 	@param eqValue	比较值
	 * 	@param func		执行函数
	 */
	void match(T value, T eqValue, FuncExecute func) {
		//	单匹配值模式如果存在匹配结果将返回
		if (!this.multiMode && this.hasMatch) {
			return ;
		}
		this.hasMatch = autoEq(value, eqValue);
		if (this.hasMatch) {
			func.execute();
		}
	}

	/**
	 * 	构建-单匹配值模式
	 *	<br>单匹配值模式:开启此模式时，在调用when()时如果已经匹配成功，将不会在继续调用下一个链式方法，调用end()时只会返回第一次的值匹配成功的结果
	 *
	 * 	@author Pan
	 * 	@param 	<T>		  	值类型
	 * 	@param 	<R>		  	返回值类型
	 * 	@return	V
	 */
	public static <T, R> ChooseEq<T, R> create() {
		return create(false);
	}

	/**
	 * 	构建-单匹配值模式
	 *	<br>单匹配值模式:开启此模式时，在调用when()时如果已经匹配成功，将不会在继续调用下一个链式方法，调用end()时只会返回第一次的值匹配成功的结果
	 *
	 * 	@author Pan
	 * 	@param 	<T>		  	值类型
	 * 	@param 	<R>		  	返回值类型
	 * 	@param 	value		默认值
	 * 	@return	V
	 */
	public static <T, R> ChooseEq<T, R> create(T value) {
		return create(value, false);
	}

	/**
	 * 	构建-自定义匹配模式
	 *	<br>多匹配值模式:开启此模式时，在调用when()时如果值已经匹配成功, 将继续调用下一个链式方法，在结尾调用end()的时候将返回最后一个值匹配结果
	 *	<br>单匹配值模式:开启此模式时，在调用when()时如果值等同的情况下，将不会在继续调用下一个链式方法，调用end()时只会返回第一次的值匹配成功的结果
	 *
	 * 	@author Pan
	 * 	@param 	<T>		  	值类型
	 * 	@param 	<R>		  	返回值类型
	 * 	@param 	multiMode	true多匹配值模式,false单匹配值模式
	 * 	@return	V
	 */
	public static <T, R> ChooseEq<T, R> create(boolean multiMode) {
		return new ChooseEq<>(multiMode);
	}

	/**
	 * 	构建-自定义匹配模式
	 *	<br>多匹配值模式:开启此模式时，在调用when()时如果值已经匹配成功, 将继续调用下一个链式方法，在结尾调用end()的时候将返回最后一个值匹配结果
	 *	<br>单匹配值模式:开启此模式时，在调用when()时如果值等同的情况下，将不会在继续调用下一个链式方法，调用end()时只会返回第一次的值匹配成功的结果
	 *
	 * 	@author Pan
	 * 	@param 	<T>		  	值类型
	 * 	@param 	<R>		  	返回值类型
	 * 	@param 	value		默认匹配值
	 * 	@param 	multiMode	true多匹配值模式,false单匹配值模式
	 * 	@return	V
	 */
	public static <T, R> ChooseEq<T, R> create(T value, boolean multiMode) {
		return new ChooseEq<>(value, multiMode);
	}

	/**
	 * 	自动匹配类型
	 *
	 * 	@author Pan
	 * 	@param 	<T>		  	值类型
	 * 	@param 	value	  	值
	 * 	@param 	eqValue	   	比较值
	 * 	@return	boolean
	 */
	public static <T> boolean autoEq(T value, T eqValue) {
		if (value == null || eqValue == null) {
			return false;
		}
		if (ClassTypeUtils.isString(value)) {
			return Eq.str((String) value, (String) eqValue);
		}
		if (ClassTypeUtils.isCollection(value)) {
			return Eq.collection((Collection) value, (Collection) eqValue);
		}
		if (ClassTypeUtils.isMap(value)) {
			return Eq.mapByKeyValue((Map) value, (Map) eqValue);
		}
		if (ClassTypeUtils.isArray(value)) {
			return autoEqArray(value, eqValue);
		}
		return Eq.object(value, eqValue);
	}

	/**
	 * 	自动匹配类型-数组
	 *
	 * 	@author Pan
	 * 	@param 	<T>		  	值类型
	 * 	@param 	value	  	值
	 * 	@param 	eqValue	   	比较值
	 * 	@return	boolean
	 */
	static <T> boolean autoEqArray(T value, T eqValue) {
		if (ClassTypeUtils.isArrayInt(value)) {
			return Eq.array((int []) value, (int []) eqValue);
		}
		if (ClassTypeUtils.isArrayLong(value)) {
			return Eq.array((long []) value, (long []) eqValue);
		}
		if (ClassTypeUtils.isArrayByte(value)) {
			return Eq.array((byte []) value, (byte []) eqValue);
		}
		if (ClassTypeUtils.isArrayChar(value)) {
			return Eq.array((char[]) value, (char []) eqValue);
		}
		if (ClassTypeUtils.isArrayShort(value)) {
			return Eq.array((short []) value, (short []) eqValue);
		}
		if (ClassTypeUtils.isArrayFloat(value)) {
			return Eq.array((float []) value, (float []) eqValue);
		}
		if (ClassTypeUtils.isArrayDouble(value)) {
			return Eq.array((double []) value, (double []) eqValue);
		}
		if (ClassTypeUtils.isArrayBoolean(value)) {
			return Eq.array((boolean []) value, (boolean []) eqValue);
		}
		return Eq.array((T[]) value, (T[]) eqValue);
	}
}
